/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Communication;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import javax.naming.CommunicationException;

/**
 *
 * @author aaron
 */
public abstract class JHaveServerLogGrepper implements LineGrepperMatchCompleteListener, LineGrepperPatternMatchListener {
    /**a shared serial number*/
    protected static int serial = 0;

    /**A singleton factory shared by all subclasses*/
    protected static LineGrepperFactory factory;

    /**The underlying linegrepper.*/
    protected LineGrepper grepper = null;

    /**The listener list for JHaveServerLogGrepperEntryFoundListeners*/
    protected javax.swing.event.EventListenerList entryFoundListenerList =
            new javax.swing.event.EventListenerList();

    /**The listener list for JHaveServerLogGrepperEntryBeganMatchingListeners*/
    protected javax.swing.event.EventListenerList entryBeganMatchingListenerList =
            new javax.swing.event.EventListenerList();

    static {
        try {
            Properties properties = new Properties();
            InputStream s = JHaveServerLogGrepper.class.getResource("JHaveServerLogGreppers.properties").openStream();
            properties.load(s);
            s.close();
            JHaveServerLogGrepper.factory = new LineGrepperFactory(properties);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(JHaveServerLogGrepper.class.getName()).log(Level.SEVERE, null, ex);
            JHaveServerLogGrepper.factory = null;
        } catch (IOException ex) {
            Logger.getLogger(JHaveServerLogGrepper.class.getName()).log(Level.SEVERE, null, ex);
            JHaveServerLogGrepper.factory = null;
        }
    }


    /**Creates a JHaveServerLogGrepper.  Registers as a listener with the underlying grepper.
     * @param grepper the grepper to set as the underlying grepper.
     */
    protected JHaveServerLogGrepper(LineGrepper grepper) {
        this.grepper = grepper;
        this.grepper.addMatchCompleteListener(this);
        this.grepper.addPatternMatchListener(this);
    }

    /**Add a listener that will be notified when a log entry is found.
     * @param listener listener to add
     */
    public void addEntryFoundListener(JHaveServerLogGrepperEntryFoundListener listener) {
        this.entryFoundListenerList.add(JHaveServerLogGrepperEntryFoundListener.class, listener);
    }

    /**Remove a listener from being notified when a log entry is found.
     * @param listener listener to remove
     */
    public void removeEntryFoundListener(JHaveServerLogGrepperEntryFoundListener listener) {
        this.entryFoundListenerList.remove(JHaveServerLogGrepperEntryFoundListener.class, listener);
    }

    /**Add a listener that will be notified when a log entry begins matching.
     * @param listener listener to add
     */
    public void addEntryBeganMatchingListener(JHaveServerLogGrepperEntryBeganMatchingListener listener) {
        this.entryBeganMatchingListenerList.add(JHaveServerLogGrepperEntryBeganMatchingListener.class, listener);
    }

    /**Remove a listener from being notified when a log entry begins matching.
     * @param listener listener to remove
     */
    public void removeEntryBeganMatchingListener(JHaveServerLogGrepperEntryBeganMatchingListener listener) {
        this.entryBeganMatchingListenerList.remove(JHaveServerLogGrepperEntryBeganMatchingListener.class, listener);
    }

    /**Fires the match complete event to listeners. (called by subclasses)
     * @param evt the event to transmit
     */
    protected void fireEntryFoundEvent(JHaveServerLogEntryFoundEvent evt) {
        Object[] listeners = this.entryFoundListenerList.getListenerList();
        for(Object listener : listeners) {
            if(listener instanceof JHaveServerLogGrepperEntryFoundListener) {
                ((JHaveServerLogGrepperEntryFoundListener)listener).jHaveServerLogEntryFound(evt);
            }
        }
    }

    /**Fires the match began event to listeners. (called by subclasses)
     * @param evt the event to transmit
     */
    protected void fireEntryBeganMatchingEvent(JHaveServerLogEntryBeganMatchingEvent evt) {
        Object[] listeners = this.entryBeganMatchingListenerList.getListenerList();
        for(Object listener : listeners) {
            if(listener instanceof JHaveServerLogGrepperEntryBeganMatchingListener) {
                ((JHaveServerLogGrepperEntryBeganMatchingListener)listener).jHaveServerLogEntryBeganMatching(evt);
            }
        }
    }

    /**Passes text into underlying grepper to match log entry
     * @see Communication.LineGrepper#inputLine(java.lang.String)
     * @param lineOfText line to test for match
     * @return true if matching is complete
     */
    public boolean inputLine(String lineOfText) {
        return this.grepper.inputLine(lineOfText);
    }

    /**Checks the underlying grepper to see if this log entry has been completely found.
     * @see Communication.LineGrepper#isMatchingComplete()
     * @return true if matching is complete
     */
    public boolean isMatchingComplete() {
        return this.grepper.isMatchingComplete();
    }

    /**The method specified by {@link Communication.LineGrepperMatchCompleteListener LineGrepperMatchCompleteListener}
     * @param evt the event object
     */
    public void lineGrepperMatchComplete(LineGrepperMatchCompleteEvent evt) {
        this.fireEntryFoundEvent(new JHaveServerLogEntryFoundEvent(this));
    }

    /**The method specified by (@link Communication.LineGrepperPatternMatchListener LineGrepperPatternMatchListener}
     * @param evt the event object
     */
    public void lineGrepperPatternMatched(LineGrepperPatternMatchEvent evt) {
        this.fireEntryBeganMatchingEvent(new JHaveServerLogEntryBeganMatchingEvent(this));
        this.grepper.removePatternMatchListener(this);
    }



    /**Gets the underlying grepper.
     * @return the underlying grepper
     */
    public LineGrepper getLineGrepper() {
        return this.grepper;
    }

    /** Return the timestamp of this log entry.  If there are multiple log lines, the timestamp is
     * read from the first line.
     * @return the timestamp of the log entry
     * @throws ParseException there is a problem with the date string matched
     * @throws JHaveServerLogGrepperMatchNotFinishedException the grepper has not finished matching yet
     */
    public Date getDate() throws ParseException, JHaveServerLogGrepperMatchNotFinishedException {
        this.throwExceptionIfMatchingIsNotComplete();
        Matcher firstMatch = this.grepper.getMatchingMatchers()[0];
        int month = groupToInt(firstMatch, 1);
        int day = groupToInt(firstMatch, 2);
        int year = groupToInt(firstMatch, 3);
        year = (year < 100 ? year + 2000 : year);
        int hour = groupToInt(firstMatch, 4);
        int minute = groupToInt(firstMatch, 5);
        String dayOrNight = firstMatch.group(6);
        String dateString = String.format("%02d/%02d/%d %02d:%02d %s",
                month, day, year, hour, minute, dayOrNight);
        SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy hh:mm a");
            
        return formatter.parse(dateString);
    }

    /**Get the date using {@link Communication.JHaveServerLogGrepper#getDate() getDate} and convert it to a string.
     * Add a serial number for the seconds to make records more unique, but still sequential.
     * @see Communication.JHaveServerLogGrepper#getDate()
     * @return String representing the date of this log entry in yyyy-MM-DD HH:mm format for SQL entry
     * @throws ParseException there is a problem with the date string matched
     * @throws JHaveServerLogGrepperMatchNotFinishedException the grepper has not finished matching yet
     */
    public String getDateString() throws ParseException, JHaveServerLogGrepperMatchNotFinishedException {
        Date d = this.getDate();
        SimpleDateFormat formatter = new SimpleDateFormat(String.format("yyyy-MM-dd HH:mm:%02d",(serial=(serial + 1)%60)));
        return formatter.format(d);
    }

    protected void throwExceptionIfMatchingIsNotComplete() throws JHaveServerLogGrepperMatchNotFinishedException {
        if(!this.isMatchingComplete()) {
            throw new JHaveServerLogGrepperMatchNotFinishedException("The log entry has not been found yet.");
        }
    }

    /**Extracts a group match from a matcher and converts the string to an int
     * @param m The matcher
     * @param groupNumber The group number
     * @return The integer
     */
    protected static int groupToInt(Matcher m, int groupNumber) {
        return(Integer.parseInt(m.group(groupNumber)));
    }

}
